{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "TODO: JS (DYNAMICAL VIEW)  \n",
    "A HIDDEN PROBLEM IN THE DOCUMENT: 英美文学史、英美文学选读 WERE RELIED ON, BUT THERE ARE NO ENTRIES FOR THEM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = {}\n",
    "\n",
    "def add(t, x, *a):\n",
    "    d[t] = x.split('\\n')\n",
    "    if a: add(*a)\n",
    "\n",
    "add('通识必修课程', \"\"\"马克思主义基本原理\n",
    "毛泽东思想和中国特色社会主义理论体系概论\n",
    "中国近现代史纲要\n",
    "思想道德修养与法律基础\n",
    "形势与政策\n",
    "社会实践（网络教学）\n",
    "社会实践（课外教学）\n",
    "军训与军事理论\n",
    "体育（1）\n",
    "体育（2）\n",
    "体育（3）\n",
    "体育（4）\n",
    "计算机基础\n",
    "创新创业基础\n",
    "第二外语（1）\n",
    "第二外语（2）\n",
    "第二外语（3）\n",
    "人文科学\n",
    "社会科学\n",
    "自然科学\"\"\", '大类必修课程', \"\"\"中级交际英语（1）\n",
    "英语语音\n",
    "基础阅读（1）\n",
    "听力技巧\n",
    "中级交际英语（2）\n",
    "基础阅读（2）\n",
    "学术听力\n",
    "中级写作（1）\n",
    "高级交际英语（1）\n",
    "演讲与辩论技巧\n",
    "分析性阅读（1）\n",
    "英语文化听力（1）\n",
    "中级写作（2）\n",
    "高级交际英语（2）\n",
    "分析性阅读（2）\n",
    "英语文化听力（2）\"\"\", '专业必修课程', \"\"\"语言学导论\n",
    "高级写作\n",
    "高级英语（1）\n",
    "笔译（1）\n",
    "口译（1）\n",
    "英美文学（1）\n",
    "当代英国社会与文化\n",
    "高级英语（2）\n",
    "笔译（2）\n",
    "口译（2）\n",
    "英美文学（2）\n",
    "当代美国社会与文化\n",
    "英语作品赏析\n",
    "外国报刊选读\n",
    "学术论文写作\n",
    "专业实习\n",
    "毕业论文\"\"\", '专业选修课组1', \"\"\"笔译101\n",
    "口译101\n",
    "英语经典文学导读\n",
    "希腊罗马神话\n",
    "英语戏剧\n",
    "英语诗歌\n",
    "小剧场\n",
    "戏剧工作坊\n",
    "文学，艺术与哲学：解读西方文化经典\n",
    "西方文学文化中的中国形象\n",
    "中国文化\n",
    "欧洲文化\n",
    "电影基础与鉴赏\n",
    "全球化\n",
    "中西社会心理对比\n",
    "批判性思维\n",
    "文化与旅游\n",
    "儿童语言习得\n",
    "人与语言\n",
    "英语词汇学\n",
    "高级英语语音\n",
    "英语语法\n",
    "语言与社会\n",
    "数字媒体艺术设计\n",
    "动态网页设计\n",
    "电子商务\n",
    "高级演讲与辩论工作坊\"\"\", '高级翻译方向选修课组2', \"\"\"文体与翻译\n",
    "连传技巧\n",
    "翻译研究概论\n",
    "专题口译\n",
    "同声传译\n",
    "笔译工作坊\n",
    "笔译（3）\n",
    "口译（3）\n",
    "商务笔译\n",
    "戏剧翻译赏析\n",
    "双语认知与翻译决策\n",
    "政治外交口译\n",
    "口译工作坊\n",
    "媒体翻译\n",
    "美国文化与成长小说\"\"\", '英美文学方向选修课组2', \"\"\"美国文化与成长小说\n",
    "英语短篇小说\n",
    "美国幽默\n",
    "20世纪英国文学\n",
    "当代西方文学批评入门\n",
    "英语文学专题\n",
    "西方思想经典导读\n",
    "英美短篇小说的叙事学阅读\n",
    "英爱戏剧批评\n",
    "二十一世纪西方文学理论导读\n",
    "莎士比亚戏剧\"\"\", '文化与传播方向选修课组2', \"\"\"传播学概论与实务\n",
    "文化研究概论\n",
    "跨文化交际\n",
    "电影文化\n",
    "广告文化\n",
    "大众传播与社会\n",
    "政治哲学导论\n",
    "正义理论入门\n",
    "美国文化与成长小说\n",
    "视觉文化与广告\n",
    "旅游会展翻译与实践\n",
    "旅游与会展大数据\"\"\", '语言学方向选修课组2', \"\"\"句法学基础\n",
    "语用学基础\n",
    "语音学和音系学基础\n",
    "语义学基础\n",
    "网络与多媒体应用\n",
    "二语习得\n",
    "汉英语言对比与翻译批评\n",
    "语言学实验与统计\n",
    "外语教学与测试\n",
    "语言认知：人脑与计算机\n",
    "话语分析\n",
    "形态学基础\"\"\", \"语言信息管理方向选修课组2\", \"\"\"VB.NET程序设计\n",
    "网站设计与管理\n",
    "数据库原理和应用\n",
    "面向对象程序设计\n",
    "移动开发与应用\n",
    "技术文献翻译\n",
    "信息技术英语口译\n",
    "管理信息系统\"\"\",\n",
    "   \n",
    "   '高翻、文学、文传共享', '美国文化与成长小说')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dup! 美国文化与成长小说\n",
      "dup! 美国文化与成长小说\n",
      "dup! 美国文化与成长小说\n"
     ]
    }
   ],
   "source": [
    "revtable = {}\n",
    "\n",
    "for k, v in d.items():\n",
    "    for i in v:\n",
    "        if i in revtable:\n",
    "            print('dup! {}'.format(i))\n",
    "        revtable[i] = k\n",
    "\n",
    "def make_table(*args):\n",
    "    return dict(zip(d.keys(), args))\n",
    "        \n",
    "colour_table = make_table(\"black\", \"black\", \"red\",   \"blue\",    \"black\", \"black\", \"red\", \"blue\", \"green\", \"red\")\n",
    "fill_colour = make_table(\"brown:bisque\", \"bisque:red\", \"red:blue\", \"blue:cyan\", \"cyan:gold\", \"gold:red\", \"red:blue\",\n",
    "                         \"blue:green\", \"green:red\", \"red:gray\")\n",
    "style_table = make_table(\"filled\", \"filled\", \"filled\",   \"filled\",    \"filled\", \"filled\", \"filled\", \"filled\", \"filled\", \"filled\")\n",
    "shape_table = make_table(\"egg\", \"polygon\", \"ellipse\",   \"triangle\",    \"diamond\", \"house\", \"hexagon\", \"doubleoctagon\",\"trapezium\", \"Mdiamond\")\n",
    "font_colour = make_table(\"black\", \"black\", \"black\",   \"white\",    \"black\", \"black\", \"white\", \"white\", \"white\", \"black\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from graphviz import Digraph\n",
    "import PyPDF2, re, io, itertools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_colour(setting=None):\n",
    "    if setting != None:\n",
    "        is_colour.colour = setting\n",
    "    return is_colour.colour"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class graph(Digraph):    \n",
    "    def node(self, x):\n",
    "        Digraph.node(self, name=x, fontname=\"Microsoft YaHei\")\n",
    "    \n",
    "    def attr_label(self, x, fontsize=25):\n",
    "        self.attr(label=x, fontname=\"Microsoft YaHei\")\n",
    "        self.attr(fontsize=str(fontsize))\n",
    "    \n",
    "    def attr_node(self, s):\n",
    "        colour = is_colour()\n",
    "        if colour: self.attr('node', style='filled', gradientangle='90')\n",
    "            \n",
    "        if s in revtable:\n",
    "            s = revtable[s]\n",
    "            self.attr('node', shape=shape_table[s])\n",
    "            if colour: self.attr('node', fontcolor=font_colour[s], fillcolor=fill_colour[s])\n",
    "        else:\n",
    "            self.attr('node', shape=list(shape_table.values())[-1])\n",
    "            if colour:\n",
    "                    self.attr('node', fontcolor=list(font_colour.values())[-1],\n",
    "                              fillcolor=list(fill_colour.values())[-1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "class graph_generator:\n",
    "    def put_nodes(self):\n",
    "        for k in revtable.keys():\n",
    "            self.g.attr_node(k)\n",
    "            self.g.node(k)\n",
    "\n",
    "    def notice(self, x):\n",
    "        print('[Notice {}] {}'.format(self.line_number, x))\n",
    "            \n",
    "    def edge(self, a, b):\n",
    "        def translate(x):\n",
    "            b = re.compile('《([^》]+)》')\n",
    "            m = b.match(x)\n",
    "\n",
    "            if m:\n",
    "                x = m.group(1)\n",
    "            if '高级交际英语2' in x:\n",
    "                self.notice(x)\n",
    "                x = '高级交际英语（2）'\n",
    "            return x\n",
    "\n",
    "        a, b = translate(a), translate(b)\n",
    "\n",
    "        self.g.attr_node(a)\n",
    "        self.g.node(a)\n",
    "\n",
    "        self.g.node(b)\n",
    "\n",
    "        try:\n",
    "            self.g.attr('edge', style=style_table[revtable[b]])\n",
    "            self.g.attr('edge', color=colour_table[revtable[b]])\n",
    "        except KeyError as e:\n",
    "            print('KeyError: {}'.format(e))\n",
    "\n",
    "        self.g.edge(a, b)\n",
    "\n",
    "    def work(self, c):\n",
    "        def translate(s):\n",
    "            if s.strip() == '无':\n",
    "                s = [ '无先修课程要求' ]\n",
    "            elif '；' in s:\n",
    "                self.notice(s)\n",
    "                s = s.split('；')\n",
    "            elif '，' in s:\n",
    "                self.notice(s)\n",
    "                s = s.split('，')\n",
    "            elif '和' in s and '数据库原理和应用' not in s:\n",
    "                self.notice(s)\n",
    "                s = s.split('和')\n",
    "            else:\n",
    "                s = [ s ]\n",
    "\n",
    "            if '（2）' in s[0]:\n",
    "                b = re.compile('(.+?)（1）(.+)?（2）')\n",
    "                m = b.match(s[0])\n",
    "\n",
    "                if m:\n",
    "                    self.notice(s[0])\n",
    "                    c = m.group(1)\n",
    "                    s[0] = c + '（1）》'\n",
    "                    s.append(c + '（2）》')\n",
    "            elif s == [ '英美文学史、英美文学选读' ]:\n",
    "                s = s[0].split('、')\n",
    "\n",
    "            return s\n",
    "\n",
    "        while True:\n",
    "            s = self.readline()\n",
    "            r = re.compile('先修课程：(.+)')\n",
    "            m = r.match(s)\n",
    "            \n",
    "            if m or '参考文献' in s: # 《美国幽默》无“先修课程”项，注意报告\n",
    "                s = translate(m.group(1)) if m else translate('无')\n",
    "\n",
    "                for i in s:\n",
    "                    if '中际交际英语' in i:\n",
    "                        i = i.replace('中际交际英语', '中级交际英语')\n",
    "                    self.edge(i, c)\n",
    "\n",
    "                break\n",
    "    \n",
    "    def readline(self):\n",
    "        self.line_number += 1\n",
    "        return self.f.readline()\n",
    "    \n",
    "    def gen(self, l):\n",
    "        self.g = graph(l)\n",
    "        print('Working: {}'.format(l))\n",
    "        is_colour('彩色' in l)\n",
    "        self.g.attr_label(l, 60)\n",
    "\n",
    "        with self.g.subgraph() as a:\n",
    "            a.attr_label('美国文化与成长小说')\n",
    "        self.g.graph_attr['rankdir'] = 'LR'\n",
    "\n",
    "        self.line_number = 0\n",
    "        self.f = open('英文学院2019版人才培养方案(0430复核定稿）.txt')\n",
    "\n",
    "        self.put_nodes()\n",
    "\n",
    "        while True:\n",
    "            s = self.readline()\n",
    "            if s == '': break\n",
    "            q = re.compile('《([^》]+?)》(课程)?教学大纲')\n",
    "            p = re.compile('\\t\\\\d+')\n",
    "            m = q.match(s.strip())\n",
    "            \n",
    "            if m and not p.search(s):\n",
    "                course = m.group(1)\n",
    "                self.work(course)\n",
    "\n",
    "        self.f.close()\n",
    "\n",
    "        self.put_nodes()\n",
    "\n",
    "        if '无分类' not in l:\n",
    "            for k, v in d.items():\n",
    "                with self.g.subgraph(name=\"cluster_\" + k) as c:\n",
    "                    c.attr_label(k)\n",
    "                    for i in v:\n",
    "                        if \"美国文化与成长小说\" == i and \"共享\" not in k: continue\n",
    "                        c.attr_node(i)\n",
    "                        c.node(i)\n",
    "\n",
    "        return self.g.pipe()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def make_pdf():\n",
    "    g = graph_generator()\n",
    "    opened_files = [ io.BytesIO(g.gen('先修课程依赖关系图（19级）{}'.format(i)))\n",
    "                        for i in itertools.product([ '分类', '无分类' ], [ '黑白', '彩色' ]) ]\n",
    "    \n",
    "    pdfFM = PyPDF2.PdfFileMerger()\n",
    "    for file in opened_files: pdfFM.append(file)\n",
    "    with open('先修课程依赖关系图（19级）（加分类）.pdf', 'wb') as write_out_file:\n",
    "        pdfFM.write(write_out_file)\n",
    "\n",
    "    for file in opened_files: file.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Working: 先修课程依赖关系图（19级）('分类', '黑白')\n",
      "[Notice 2709] 《中级交际英语（1）、（2）》\n",
      "[Notice 2741] 《中际交际英语（1）、（2）》\n",
      "[Notice 2780] 《基础阅读（1）、（2）》\n",
      "[Notice 2994] 《中级写作（1）、（2）》\n",
      "[Notice 4324] 《笔译（1）》；《文体与翻译》\n",
      "[Notice 4366] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4397] 《口译（1）》；《口译（2）》；《连传技巧》；《专题口译》\n",
      "[Notice 4428] 《笔译（1）》；《笔译（2）》；《文体与翻译》；《翻译研究概论》\n",
      "[Notice 4461] 《笔译（1）》；《笔译（2）》\n",
      "[Notice 4493] 《口译（1）》，《口译（2）》\n",
      "[Notice 4622] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4840] 《英美文学（1）、（2）》\n",
      "[Notice 4868] 《英美文学（1）、（2）》\n",
      "[Notice 5018] 《英美文学（1）（2）》和《当代西方文学批评入门》\n",
      "[Notice 5018] 《英美文学（1）（2）》\n",
      "[Notice 5078] 高级交际英语2\n",
      "[Notice 5109] 高级交际英语2\n",
      "[Notice 5137] 高级交际英语2\n",
      "[Notice 5275] 高级交际英语2\n",
      "[Notice 5633] 《语言学导论》；《笔译（1）》\n",
      "Working: 先修课程依赖关系图（19级）('分类', '彩色')\n",
      "[Notice 2709] 《中级交际英语（1）、（2）》\n",
      "[Notice 2741] 《中际交际英语（1）、（2）》\n",
      "[Notice 2780] 《基础阅读（1）、（2）》\n",
      "[Notice 2994] 《中级写作（1）、（2）》\n",
      "[Notice 4324] 《笔译（1）》；《文体与翻译》\n",
      "[Notice 4366] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4397] 《口译（1）》；《口译（2）》；《连传技巧》；《专题口译》\n",
      "[Notice 4428] 《笔译（1）》；《笔译（2）》；《文体与翻译》；《翻译研究概论》\n",
      "[Notice 4461] 《笔译（1）》；《笔译（2）》\n",
      "[Notice 4493] 《口译（1）》，《口译（2）》\n",
      "[Notice 4622] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4840] 《英美文学（1）、（2）》\n",
      "[Notice 4868] 《英美文学（1）、（2）》\n",
      "[Notice 5018] 《英美文学（1）（2）》和《当代西方文学批评入门》\n",
      "[Notice 5018] 《英美文学（1）（2）》\n",
      "[Notice 5078] 高级交际英语2\n",
      "[Notice 5109] 高级交际英语2\n",
      "[Notice 5137] 高级交际英语2\n",
      "[Notice 5275] 高级交际英语2\n",
      "[Notice 5633] 《语言学导论》；《笔译（1）》\n",
      "Working: 先修课程依赖关系图（19级）('无分类', '黑白')\n",
      "[Notice 2709] 《中级交际英语（1）、（2）》\n",
      "[Notice 2741] 《中际交际英语（1）、（2）》\n",
      "[Notice 2780] 《基础阅读（1）、（2）》\n",
      "[Notice 2994] 《中级写作（1）、（2）》\n",
      "[Notice 4324] 《笔译（1）》；《文体与翻译》\n",
      "[Notice 4366] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4397] 《口译（1）》；《口译（2）》；《连传技巧》；《专题口译》\n",
      "[Notice 4428] 《笔译（1）》；《笔译（2）》；《文体与翻译》；《翻译研究概论》\n",
      "[Notice 4461] 《笔译（1）》；《笔译（2）》\n",
      "[Notice 4493] 《口译（1）》，《口译（2）》\n",
      "[Notice 4622] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4840] 《英美文学（1）、（2）》\n",
      "[Notice 4868] 《英美文学（1）、（2）》\n",
      "[Notice 5018] 《英美文学（1）（2）》和《当代西方文学批评入门》\n",
      "[Notice 5018] 《英美文学（1）（2）》\n",
      "[Notice 5078] 高级交际英语2\n",
      "[Notice 5109] 高级交际英语2\n",
      "[Notice 5137] 高级交际英语2\n",
      "[Notice 5275] 高级交际英语2\n",
      "[Notice 5633] 《语言学导论》；《笔译（1）》\n",
      "Working: 先修课程依赖关系图（19级）('无分类', '彩色')\n",
      "[Notice 2709] 《中级交际英语（1）、（2）》\n",
      "[Notice 2741] 《中际交际英语（1）、（2）》\n",
      "[Notice 2780] 《基础阅读（1）、（2）》\n",
      "[Notice 2994] 《中级写作（1）、（2）》\n",
      "[Notice 4324] 《笔译（1）》；《文体与翻译》\n",
      "[Notice 4366] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4397] 《口译（1）》；《口译（2）》；《连传技巧》；《专题口译》\n",
      "[Notice 4428] 《笔译（1）》；《笔译（2）》；《文体与翻译》；《翻译研究概论》\n",
      "[Notice 4461] 《笔译（1）》；《笔译（2）》\n",
      "[Notice 4493] 《口译（1）》，《口译（2）》\n",
      "[Notice 4622] 《口译（1）》；《连传技巧》 \n",
      "[Notice 4840] 《英美文学（1）、（2）》\n",
      "[Notice 4868] 《英美文学（1）、（2）》\n",
      "[Notice 5018] 《英美文学（1）（2）》和《当代西方文学批评入门》\n",
      "[Notice 5018] 《英美文学（1）（2）》\n",
      "[Notice 5078] 高级交际英语2\n",
      "[Notice 5109] 高级交际英语2\n",
      "[Notice 5137] 高级交际英语2\n",
      "[Notice 5275] 高级交际英语2\n",
      "[Notice 5633] 《语言学导论》；《笔译（1）》\n"
     ]
    }
   ],
   "source": [
    "make_pdf()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
